home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / faxmail / faxmail.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  18KB  |  748 lines

  1. /*    $Header: /usr/people/sam/fax/faxmail/RCS/faxmail.c++,v 1.26 1994/04/26 18:20:16 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. #include "Str.h"
  26. #include "StackBuffer.h"
  27. #include "PageSize.h"
  28.  
  29. #include <time.h>
  30. #include <stdlib.h>
  31. #include <stdarg.h>
  32. #include <ctype.h>
  33. #include <stdio.h>
  34. #include <string.h>
  35. #include <math.h>
  36. #include <errno.h>
  37. #include <dirent.h>
  38.  
  39. #include <pwd.h>
  40. #include <osfcn.h>
  41. #include <fcntl.h>
  42.  
  43. extern void fxFatal(const char* fmt, ...);
  44.  
  45. static    const NCHARS = 256;        // chars per font
  46.  
  47. struct Font {
  48.     fxStr    name;            // font name
  49.     fxStr    showproc;        // PostScript show operator
  50.     float    widths[NCHARS];        // width table
  51.  
  52.     static const fxStr fontDir;
  53.  
  54.     Font();
  55.  
  56.     void show(FILE*, const char*, int len);
  57.     void show(FILE*, const fxStr&);
  58.     float strwidth(const char*);
  59.  
  60.     void setDefaultMetrics(float pointsize);
  61.     FILE* openFile(fxStr& pathname);
  62.     fxBool readMetrics(float pointsize);
  63.     fxBool getAFMLine(FILE* fp, char* buf, int bsize);
  64. };
  65. const fxStr Font::fontDir = FONTDIR;
  66.  
  67. class faxMailApp {
  68. private:
  69.     fxStr    appName;        // for error messages
  70.     long    pagefactor;        // n-up factor
  71.     int        row, col;        // row+column for n-up
  72.     int        pageno;            // page number
  73.  
  74.     fxBool    lastHeaderShown;    // header is shown
  75.     fxBool    doBodyFont;        // look for body font in mail headers
  76.     fxBool    wrapLines;        // wrap/truncate long lines
  77.     fxBool    startNewPage;        // emit new page prologue
  78.  
  79.     char    buffer[2048];        // input buffer (mail)
  80.     fxStr    output;            // output buffer (postscript)
  81.  
  82.     float    pageWidth, pageHeight;    // physical page dimensions
  83.     float    pagewidth, pageheight;    // imaged page dimensions
  84.     float    xMargin, yMargin;    // horizontal+vertical margins
  85.     float    ymax, ymin;        // max/min vertical spots for text
  86.     float    xmax;            // max horizontal spot for text
  87.     float    xpos, ypos;        // current x-y spot on logical page
  88.     float    vh;            // vertical height of a line
  89.     float    tabWidth;        // width in points of tab
  90.     float    headerStop;        // tab stop for headers
  91.  
  92.     float    pointsize;        // font size
  93.     Font    italic;            // italic font info
  94.     Font    bold;            // bold font info
  95.     Font    body;            // body font info
  96.     fxStr    defBodyFont;        // fallback font to use for body
  97.     fxStr    bodyFont;        // body font name
  98.     fxStr    bodyFontFile;        // file holding body font
  99.  
  100.     static const char* headers[];
  101.  
  102.     void formatMail(FILE*);
  103.     void flushHeaderLine(FILE*, const fxStr&);
  104.     void calculateHeaderStop();
  105.     fxBool okToShowHeader(const char* tag);
  106.  
  107.     void defineFont(FILE* fd, const char* psname, const char* fname);
  108.     void startLogicalPage(FILE*);
  109.     void endLogicalPage(FILE*);
  110.     void tab(FILE*);
  111.     void hmove(FILE*, float pos);
  112.     void lineBreak(FILE*);
  113.     void flushOutput(FILE*);
  114.     void showWrapItalic(FILE* fd, const char* cp);
  115.     void setupPageSize(const char* name);
  116.  
  117.     void lookForBodyFont(const char* fromLine);
  118.     fxBool loadBodyFont(FILE*, const char* fileName, const char* fontName);
  119.  
  120.     void usage();
  121.     void printError(const char* va_alist ...);
  122.     void fontWarn(const fxStr& name);
  123. public:
  124.     faxMailApp();
  125.     ~faxMailApp();
  126.  
  127.     void initialize(int argc, char** argv);
  128.     void open();
  129. };
  130.  
  131. const char* faxMailApp::headers[] = {
  132.     "To",
  133.     "Subject",
  134.     "From",
  135.     "Date",
  136.     "Cc",
  137.     "Summary",
  138.     "Keywords",
  139. };
  140. #define    NHEADERS \
  141.     (sizeof (faxMailApp::headers) / sizeof (faxMailApp::headers[0]))
  142.  
  143. faxMailApp::faxMailApp()
  144. {
  145.     bold.name = "Helvetica-Bold";
  146.     italic.name = "Helvetica-Oblique";
  147.     defBodyFont = "Courier-Bold";
  148.     body.name = defBodyFont;
  149.     doBodyFont = TRUE;
  150.     wrapLines = TRUE;
  151.     pagefactor = 1;
  152.     pointsize = 12.;            // medium size
  153.     xMargin = .35;            // .35" works ok with 12 point font
  154.     yMargin = .5;            // .5" top+bottom margins
  155.     pageno = 0;                // 1 input page per output page
  156. }
  157.  
  158. faxMailApp::~faxMailApp()
  159. {
  160. }
  161.  
  162. void
  163. faxMailApp::initialize(int argc, char** argv)
  164. {
  165.     extern int optind;
  166.     extern char* optarg;
  167.     int c;
  168.  
  169.     appName = argv[0];
  170.     u_int l = appName.length();
  171.     appName = appName.tokenR(l, '/');
  172.  
  173.     setupPageSize("default");
  174.     while ((c = getopt(argc, argv, "b:H:i:f:F:p:s:W:x:y:1234nw")) != -1)
  175.     switch (c) {
  176.     case 'b':            // bold font for headers
  177.         bold.name = optarg;
  178.         break;
  179.     case 'H':            // page height
  180.         pageHeight = atof(optarg);
  181.         break;
  182.     case 'i':            // italic font for headers
  183.         italic.name = optarg;
  184.         break;
  185.     case 'f':            // default font for text body
  186.         bodyFont = optarg;
  187.         break;
  188.     case 'F':            // special body font
  189.         bodyFontFile = optarg;
  190.         doBodyFont = FALSE;
  191.         break;
  192.     case 'n':            // no special body font
  193.         doBodyFont = FALSE;
  194.         break;
  195.     case 'p':            // point size
  196.         pointsize = atof(optarg);
  197.         break;
  198.     case 's':            // page size
  199.         setupPageSize(optarg);
  200.         break;
  201.     case 'W':            // page width
  202.         pageWidth = atof(optarg);
  203.         break;
  204.     case 'x':            // horizontal margins
  205.         xMargin = atof(optarg);
  206.         break;
  207.     case 'y':            // vertical margins
  208.         yMargin = atof(optarg);
  209.         break;
  210.     case '1': case '2': case '3': case '4':
  211.         pagefactor = c - '0';
  212.         break;
  213.     case 'w':
  214.         wrapLines = FALSE;
  215.         break;
  216.     case '?':
  217.         usage();
  218.         /*NOTREACHED*/
  219.     }
  220.  
  221.     pagewidth = pageWidth - 2*xMargin;
  222.     pageheight = pageHeight - 2*yMargin;
  223.  
  224.     // vertical height of a line XXX should use descender info
  225.     vh = (pageHeight+.018*pageHeight)/(pageHeight-.018*pageHeight)*pointsize;
  226.     xmax = 72 * pagewidth;    // max horizontal place
  227.     ymax = 72 * pageheight;    // max vertical location
  228.     ymin = 0;            // lowest imageable point on page
  229.  
  230.     if (!italic.readMetrics(pointsize))
  231.     fontWarn(italic.name);
  232.     if (!bold.readMetrics(pointsize))
  233.     fontWarn(bold.name);
  234.     calculateHeaderStop();
  235.  
  236.     if (bodyFontFile != "") {
  237.     u_int l = bodyFontFile.length();
  238.     bodyFont = bodyFontFile.tokenR(l, '/');        // last component
  239.     l = bodyFont.nextR(bodyFont.length(), '.');    // strip any suffix
  240.     if (l > 0)
  241.         bodyFont.resize(l-1);
  242.     }
  243. }
  244.  
  245. void
  246. faxMailApp::setupPageSize(const char* name)
  247. {
  248.     PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
  249.     if (!info)
  250.     fxFatal("Unknown page size \"%s\"", name);
  251.     pageWidth = info->width() / 25.4;
  252.     pageHeight = info->height() / 25.4;
  253.     delete info;
  254. }
  255.  
  256. static const char* prolog =
  257. "/inch {72 mul} def\n"
  258. "/pw %.2f inch def\n"
  259. "/ph %.2f inch def\n"
  260. "/nl {"
  261.     "/vpos vpos %.2f sub def\n"
  262.     "0 vpos moveto\n"
  263. "} bind def\n"
  264. "/hm {"
  265.     "vpos moveto\n"
  266. "} bind def\n"
  267. "/pagetop {"
  268.     "/vpos %.2f def\n"
  269.     "0 vpos moveto\n"
  270. "} bind def\n"
  271. "/outline {\n"
  272.     ".1 setlinewidth\n"
  273.     "0.8 setgray\n"
  274.     "gsave\n"
  275.     ".1 inch -.1 inch translate\n"
  276.     "newpath\n"
  277.     "0 0 moveto\n"
  278.     "0 ph lineto\n"
  279.     "pw ph lineto\n"
  280.     "pw 0 lineto\n"
  281.     "closepath\n"
  282.     "fill\n"
  283.     "grestore\n"
  284.     "1.0 setgray\n"
  285.     "newpath\n"
  286.     "0 0 moveto\n"
  287.     "0 ph lineto\n"
  288.     "pw ph lineto\n"
  289.     "pw 0 lineto\n"
  290.     "closepath\n"
  291.     "fill\n"
  292.     "0.0 setgray\n"
  293.     "newpath\n"
  294.     "0 0 moveto\n"
  295.     "0 ph lineto\n"
  296.     "pw ph lineto\n"
  297.     "pw 0 lineto\n"
  298.     "closepath\n"
  299.     "stroke\n"
  300. "} bind def\n"
  301. "/ishow {ifont setfont show} bind def\n"
  302. "/bshow {bfont setfont show} bind def\n"
  303. "/rshow {bodyfont setfont show} bind def\n"
  304. ;
  305.  
  306. void
  307. faxMailApp::open()
  308. {
  309.     printf("%%!PS-Adobe-3.0\n");
  310.     printf("%%%%Creator: faxmail\n");
  311.     printf("%%%%Title: E-Mail\n");
  312.     time_t t = time(0);
  313.     printf("%%%%CreationDate: %s", ctime(&t));
  314.     printf("%%%%Origin: 0 0\n");
  315.     printf("%%%%BoundingBox: 0 0 %.0f %.0f\n", pageHeight*72, pageWidth*72);
  316.     printf("%%%%Pages: (atend)\n");
  317.     printf("%%%%PageOrder: Ascend\n");
  318.     printf("%%%%EndComments\n");
  319.     printf("%%%%BeginProlog\n");
  320.     printf(prolog, pageWidth, pageHeight, vh, ymax);
  321.     printf("%%%%EndProlog\n");
  322.     printf("%%%%BeginSetup\n");
  323.     defineFont(stdout, "ifont", italic.name);    italic.showproc = "ishow";
  324.     defineFont(stdout, "bfont", bold.name);    bold.showproc = "bshow";
  325.     if (bodyFontFile == "" || !loadBodyFont(stdout, bodyFontFile, bodyFont))
  326.     defineFont(stdout, "bodyfont", defBodyFont);
  327.     body.showproc = "rshow";
  328.     printf("%%%%EndSetup\n");
  329.     formatMail(stdout);
  330.     printf("%%%%Trailer\n");
  331.     printf("%%%%Pages: %d\n", pageno);
  332.     printf("%%%%EOF\n");
  333. }
  334.  
  335. fxBool
  336. faxMailApp::loadBodyFont(FILE* fd, const char* fileName, const char* fontName)
  337. {
  338.     int f = ::open(fileName, O_RDONLY);
  339.     if (f >= 0) {
  340.     char buf[16*1024];
  341.     int n;
  342.     while ((n = read(f, buf, sizeof (buf))) > 0)
  343.         fwrite(buf, n, 1, fd);
  344.     ::close(f);
  345.     defineFont(fd, "bodyfont", fontName);
  346.     return (TRUE);
  347.     } else
  348.     return (FALSE);
  349. }
  350.  
  351. void
  352. faxMailApp::formatMail(FILE* fd)
  353. {
  354.     startLogicalPage(fd);
  355.     int cc = 0;
  356.     char* bp = buffer;
  357.     int lastc = 0;
  358.     fxStr line;
  359.     for (;;) {
  360.     if (cc <= 0) {
  361.         cc = fread(buffer, 1, sizeof (buffer), stdin);
  362.         if (cc == 0)        // EOF
  363.         break;
  364.         bp = buffer;
  365.     }
  366.     int c = *bp++; cc--;
  367.     if (c == '\n') {
  368.         if (lastc == '\n')
  369.         break;
  370.         flushHeaderLine(fd, line);
  371.         line.resize(0);
  372.     } else
  373.         line.append(c);
  374.     lastc = c;
  375.     }
  376.     lineBreak(fd);
  377.     if (!body.readMetrics(pointsize))
  378.     fontWarn(body.name);
  379.     tabWidth = 8 * body.widths[' '];
  380.     for (;;) {
  381.     if (cc <= 0) {
  382.         cc = fread(buffer, 1, sizeof (buffer), stdin);
  383.         if (cc == 0)        // EOF
  384.         break;
  385.         bp = buffer;
  386.     }
  387.     int c = *bp++; cc--;
  388.     switch (c) {
  389.     case '\t':
  390.         tab(fd);
  391.         break;
  392.     case '\f':
  393.         endLogicalPage(fd);        // flush current page
  394.         startLogicalPage(fd);    // ... and being a new one
  395.         break;
  396.     case '\n':
  397.         lineBreak(fd);
  398.         break;
  399.     default:
  400.         if (c >= NCHARS)        // only accept 7-bit ascii
  401.         break;
  402.         if (startNewPage)
  403.         startLogicalPage(fd);
  404.         if (xpos + body.widths[c] > xmax) {
  405.         if (!wrapLines)        // toss character
  406.             break;
  407.         lineBreak(fd);
  408.         }
  409.         xpos += body.widths[c];
  410.         output.append(c);
  411.         break;
  412.     }
  413.     }
  414.     endLogicalPage(fd);
  415. }
  416.  
  417. void
  418. faxMailApp::flushHeaderLine(FILE* fd, const fxStr& line)
  419. {
  420.     /*
  421.      * Collect field name in a canonical format.
  422.      * If the line begins with whitespace, then
  423.      * it's the continuation of a previous header.
  424.      */ 
  425.     if (line.length() > 0 && !isspace(line[0])) { 
  426.     u_int l = 0;
  427.     fxStr field(line.token(l, ':'));
  428.     if (field == "" || l >= line.length())
  429.         return;
  430.     if (lastHeaderShown = okToShowHeader(field)) {
  431.         /*
  432.          * Embolden field name and italicize field value.
  433.          */
  434.         fxStr value(line.tail(line.length() - l));
  435.         bold.show(fd, field | ":");
  436.         hmove(fd, headerStop);
  437.         showWrapItalic(fd, value);
  438.         lineBreak(fd);
  439.         if (field == "from" && doBodyFont)
  440.         lookForBodyFont(value);
  441.     }
  442.     } else if (lastHeaderShown) {
  443.     hmove(fd, headerStop);            // line up with above header
  444.     showWrapItalic(fd, line);
  445.     lineBreak(fd);
  446.     }
  447. }
  448.  
  449. void
  450. faxMailApp::showWrapItalic(FILE* fd, const char* cp)
  451. {
  452.     while (isspace(*cp))            // skip white space
  453.     cp++;
  454.     float x = xpos;
  455.     const char* tp = cp;
  456.     for (; *tp != '\0'; tp++) {
  457.     float w = italic.widths[*tp];
  458.     if (x + w > xmax) {
  459.         italic.show(fd, cp, tp-cp);
  460.         lineBreak(fd);
  461.         hmove(fd, headerStop);
  462.         cp = tp, x = xpos;
  463.     }
  464.     x += w;
  465.     }
  466.     if (tp > cp)
  467.     italic.show(fd, cp, tp-cp);
  468. }
  469.  
  470. void
  471. faxMailApp::startLogicalPage(FILE* fd)
  472. {
  473.     if (row == 0 && col == 0) {
  474.     pageno++;
  475.     fprintf(fd, "%%%%Page: \"%d\" %d\n", pageno, pageno);
  476.     }
  477.     ypos = ymax;
  478.     xpos = 0;                // translate offsets page
  479.     fprintf(fd, "gsave\n");
  480.     if (pagefactor > 1) {
  481.     fprintf(fd, "%g inch %g inch translate\n",
  482.         xMargin + col * pagewidth / (float) pagefactor,
  483.         yMargin + (pagefactor - row - 1) * pageheight / (float) pagefactor);
  484.     fprintf(fd, "%g dup scale\n", 1./pagefactor);
  485.     } else
  486.     fprintf(fd, "%.2f inch %.2f inch translate\n", xMargin, yMargin);
  487.     if (pagefactor > 1) 
  488.     fprintf(fd, "outline\n");
  489.     fprintf(fd, "pagetop\n");        // top of page
  490.     startNewPage = FALSE;
  491. }
  492.  
  493. void
  494. faxMailApp::endLogicalPage(FILE* fd)
  495. {
  496.     flushOutput(fd);
  497.     fprintf(fd, "grestore showpage\n");
  498.     if (++row == pagefactor) {
  499.     row = 0;
  500.     if (++col == pagefactor)
  501.         col = 0;
  502.     }
  503.     startNewPage = TRUE;
  504. }
  505.  
  506. void
  507. faxMailApp::lineBreak(FILE* fd)
  508. {
  509.     flushOutput(fd);
  510.     fprintf(fd, "nl\n");
  511.     ypos -= vh;
  512.     xpos = 0;
  513.     if (ypos < ymin)
  514.     endLogicalPage(fd);
  515. }
  516.  
  517. void
  518. faxMailApp::tab(FILE* fd)
  519. {
  520.     flushOutput(fd);
  521.     if (xpos)
  522.     xpos = floor((xpos + 0.05) / tabWidth);
  523.     xpos = tabWidth * (1 + xpos);
  524.     hmove(fd, xpos);
  525. }
  526.  
  527. void
  528. faxMailApp::hmove(FILE* fd, float pos)
  529. {
  530.     flushOutput(fd);
  531.     xpos = pos;
  532.     fprintf(fd, "%.2f hm\n", xpos);
  533. }
  534.  
  535. void
  536. faxMailApp::flushOutput(FILE* fd)
  537. {
  538.     if (startNewPage)
  539.     startLogicalPage(fd);
  540.     if (output.length() > 0) {
  541.     body.show(fd, output, output.length());
  542.     output.resize(0);
  543.     }
  544. }
  545.  
  546. void
  547. faxMailApp::defineFont(FILE* fd, const char* psname, const char* fname)
  548. {
  549.     fprintf(fd, "/%s /%s findfont %.1f scalefont def\n",
  550.     psname, fname, pointsize);
  551. }
  552.  
  553. fxBool
  554. faxMailApp::okToShowHeader(const char* tag)
  555. {
  556.     for (u_int i = 0; i < NHEADERS; i++)
  557.     if (strcasecmp(headers[i], tag) == 0)
  558.         return (TRUE);
  559.     return (FALSE);
  560. }
  561.  
  562. void
  563. faxMailApp::calculateHeaderStop()
  564. {
  565.     headerStop = 0;
  566.     for (u_int i = 0; i < NHEADERS; i++) {
  567.     float w = bold.strwidth(headers[i]);
  568.     if (w > headerStop)
  569.         headerStop = w;
  570.     }
  571.     headerStop += bold.widths[':'];
  572.     float boldTab = 8 * bold.widths[' '];
  573.     headerStop = boldTab * (1 + floor((headerStop + 0.05) / boldTab));
  574. }
  575.  
  576. void
  577. faxMailApp::lookForBodyFont(const char* sender)
  578. {
  579.     for (const char* cp = sender; isspace(*cp); cp++)
  580.     ;
  581.     fxStr name(cp);
  582.     int l = name.next(0, '<');
  583.     if (l != name.length()) {    // Joe Schmo <joe@foobar>
  584.     name.remove(0, l);
  585.     name.resize(name.next(0, '>'));
  586.     } else            // joe@foobar (Joe Schmo)
  587.     name.resize(name.next(0, ' '));
  588.     if (name.length() == 0)
  589.     return;
  590.     // remove @host
  591.     name.resize(name.next(0, '@'));
  592.     // now strip leading host!host!...
  593.     while ((l = name.next(0, '!')) != name.length())
  594.     name.remove(0, l+1);
  595.     // got user account name, look for font in home directory!
  596.     struct passwd* pwd = getpwnam(name);
  597.     if (pwd) {
  598.     bodyFontFile = fxStr(pwd->pw_dir) | "/" | ".faxfont.ps";
  599.     bodyFont = name;
  600.     }
  601. }
  602.  
  603. void
  604. faxMailApp::usage()
  605. {
  606.     fxFatal("usage: %s"
  607.     " [-p pointsize]"
  608.     " [-1234]"
  609.     , (char*) appName);
  610. }
  611.  
  612. void
  613. faxMailApp::fontWarn(const fxStr& name)
  614. {
  615.     printError(
  616.     "Warning, can not open metrics for %s, using constant width instead.\n",
  617.     (char*) name);
  618. }
  619.  
  620. void
  621. faxMailApp::printError(const char* va_alist ...)
  622. #define    fmt va_alist
  623. {
  624.     va_list ap;
  625.     va_start(ap, va_alist);
  626.     fprintf(stderr, "%s: ", (char*) appName);
  627.     vfprintf(stderr, fmt, ap);
  628.     va_end(ap);
  629.     fprintf(stderr, ".\n");
  630. }
  631. #undef fmt
  632.  
  633. Font::Font()
  634. {
  635.     setDefaultMetrics(10.);        // XXX insure something is defined
  636. }
  637.  
  638. void
  639. Font::show(FILE* fd, const char* val, int len)
  640. {
  641.     if (len > 0) {
  642.     fprintf(fd, "(");
  643.     do {
  644.         if (*val == '(' || *val == ')' || *val == '\\')
  645.         putc('\\', fd);
  646.         putc(*val++, fd);
  647.     } while (--len);
  648.     fprintf(fd, ") %s\n", (char*) showproc);
  649.     }
  650. }
  651.  
  652. void
  653. Font::show(FILE* fd, const fxStr& s)
  654. {
  655.     show(fd, s, s.length());
  656. }
  657.  
  658. float
  659. Font::strwidth(const char* cp)
  660. {
  661.     float w = 0;
  662.     while (*cp)
  663.     w += widths[*cp++];
  664.     return w;
  665. }
  666.  
  667. fxBool
  668. Font::getAFMLine(FILE* fp, char* buf, int bsize)
  669. {
  670.     if (fgets(buf, bsize, fp) == NULL)
  671.     return (FALSE);
  672.     char* cp = strchr(buf, '\n');
  673.     if (cp == NULL) {            // line too long, skip it
  674.     int c;
  675.     while ((c = getc(fp)) != '\n')    // skip to end of line
  676.         if (c == EOF)
  677.         return (FALSE);
  678.     cp = buf;            // force line to be skipped
  679.     }
  680.     *cp = '\0';
  681.     return (TRUE);
  682. }
  683.  
  684. FILE*
  685. Font::openFile(fxStr& pathname)
  686. {
  687.     FILE* fd;
  688.     pathname = fontDir | "/" | name | ".afm";
  689.     fd = fopen(pathname, "r");
  690.     if (fd != NULL || errno != ENOENT)
  691.     return (fd);
  692.     pathname.resize(pathname.length()-4);
  693.     return (fopen(pathname, "r"));
  694. }
  695.  
  696. fxBool
  697. Font::readMetrics(float pointsize)
  698. {
  699.     fxStr file;
  700.     FILE *fp = openFile(file);
  701.     if (fp == NULL) {
  702.     setDefaultMetrics(pointsize);    // NB: use fixed width metrics
  703.     return (FALSE);
  704.     }
  705.     for (int i = 0; i < NCHARS; i++)
  706.     widths[i] = 0;
  707.  
  708.     char buf[1024];
  709.     u_int line = 0;
  710.     do {
  711.     if (!getAFMLine(fp, buf, sizeof (buf))) {
  712.         fclose(fp);
  713.         return (TRUE);            // XXX
  714.     }
  715.     line++;
  716.     } while (strncmp(buf, "StartCharMetrics", 16));
  717.     while (getAFMLine(fp, buf, sizeof (buf)) && strcmp(buf, "EndCharMetrics")) {
  718.     line++;
  719.     int ix, w;
  720.     /* read the glyph position and width */
  721.     if (sscanf(buf, "C %d ; WX %d ;", &ix, &w) == 2) {
  722.         if (ix == -1)        // end of unencoded glyphs
  723.         break;
  724.         if (ix < NCHARS)
  725.         widths[ix] = (w/1000.) * pointsize;
  726.     } else
  727.         fprintf(stderr, "%s, line %u: format error", (char*) file, line);
  728.     }
  729.     fclose(fp);
  730.     return (TRUE);
  731. }
  732.  
  733. void
  734. Font::setDefaultMetrics(float pointsize)
  735. {
  736.     for (int i = 0; i < NCHARS; i++)
  737.     widths[i] = .625 * pointsize;
  738. }
  739.  
  740. int
  741. main(int argc, char** argv)
  742. {
  743.     faxMailApp app;
  744.     app.initialize(argc, argv);
  745.     app.open();
  746.     return 0;
  747. }
  748.